package org.fing.edu.uy.esbadp.motor.XSLT;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.fing.edu.uy.esbadp.core.compare.wsdl.OperationDiff;
import org.fing.edu.uy.esbadp.core.compare.wsdl.ParameterDiff;
import org.fing.edu.uy.esbadp.core.compare.wsdl.ParameterDiff.TypeValue;
import org.fing.edu.uy.esbadp.core.compare.wsdl.WsdlDiff;
import org.fing.edu.uy.esbadp.core.constants.EsbAdpServiceMetadataKeys;
import org.fing.edu.uy.esbadp.core.entity.DMRegisteredService;
import org.fing.edu.uy.esbadp.logger.LoggerADP;
import org.fing.edu.uy.esbadp.motor.comunication.AdmServiceComunication;


public class XSLManager {
    
    private static final java.util.logging.Logger logger = LoggerADP.getLogger(XSLManager.class.getSimpleName());
    
    public String createXSL(WsdlDiff diff, Long serviceID, boolean isResponse) {
    	StringBuilder fileContent = new StringBuilder();
        try {
        	String prefix = diff.getNameSpace().getPrefix();
        	String namespace = diff.getNameSpace().getNamespaceURI();
           	
        	fileContent.append(XSLTemplateFunction.XSLStartTemplate(prefix, namespace));
            List<OperationDiff> operations = diff.getOperations();
            for (OperationDiff operation : operations) {
                OperationDiff.Modification modType = operation.getModificationTypeName();
            	Map<String,List<ParameterDiff>> matchedAttributes = new HashMap<String,List<ParameterDiff>>();
                switch (modType) {
                    case RENAME:
                    	fileContent.append(XSLTemplateFunction.getRenameFunctionTemplate(prefix, operation.getName(), operation.getNewName()));
                        if(operation.getParameterChanges() != null){
                        	parameterDiff(prefix,operation,matchedAttributes, fileContent,serviceID);
                        }
                        fileContent.append(XSLTemplateFunction.getActionChangeTemplate(operation.getNewName(),isResponse));
                    break;
                    
                    case DELETE:
                    	fileContent.append(XSLTemplateFunction.getDeleteFunctionTemplate(prefix,operation.getName()));
                    break;
                    
                    case ONLY_PARAMETER:
                        parameterDiff(prefix,operation,matchedAttributes, fileContent,serviceID);
                    break;
                }
            }
            fileContent.append(XSLTemplateFunction.processRenameOperations(operations, isResponse));
            fileContent.append(XSLTemplateFunction.XSLEndTemplate());
            //End new xsl 
        } catch (Exception ex) {
        	fileContent = null;
            logger.log(Level.SEVERE, "Exception in createXSL...\n StackTarce => [ {0} ]", ex);
        }
        return fileContent.toString();
    }

    /*** AUXILIARY FUNCTIONS ***/
    private void parameterDiff(String prefix, OperationDiff operation, Map<String,List<ParameterDiff>> matchedAttributes, StringBuilder fileContent, Long serviceID) {
        List<String> diffCodes = new LinkedList<String>();
    	 
        
    	List<ParameterDiff> parameterChanges = operation.getParameterChanges();
        Iterator<ParameterDiff> it = parameterChanges.iterator();
        
        while (it.hasNext()) {
            ParameterDiff parameterDiff = it.next();
            
            ParameterDiff.Modification parameterModificationType = parameterDiff.getModificationType();
            List<ParameterDiff> parameterdiffs; 
            switch (parameterModificationType) {
                case NEW:
                	
                	DMRegisteredService registeredService = AdmServiceComunication.getVirtualService(serviceID);    	
                	String newNumericValue = registeredService.getMetadata(EsbAdpServiceMetadataKeys.newNumericValue,EsbAdpServiceMetadataKeys.newNumericValueDefaultValue);
                	String newStringValue =  registeredService.getMetadata(EsbAdpServiceMetadataKeys.newStringValue,EsbAdpServiceMetadataKeys.newStringValueDefaultValue);
                    
                	
                	TypeValue type = parameterDiff.getTypeValue();
                    switch (type) {
                    	case STRING:
                    			parameterDiff.setDefaultValue(newStringValue);
                    		 	break;
                    	case NUMERIC:
                    			parameterDiff.setDefaultValue(newNumericValue);
                   		 		break;
                    	case NONE:
                    			parameterDiff.setDefaultValue("");
                   		 		break;
                    }   
                	
                    List<ParameterDiff> childParameters = new LinkedList<ParameterDiff>();
                    int index = parameterChanges.indexOf(parameterDiff);
                    childParameters.add(parameterDiff);

                    loop : while(it.hasNext()){
                        index++;
                        ParameterDiff aux = parameterChanges.get(index);
                        ParameterDiff aux2 = parameterChanges.get(index-1);
                        if(aux2 != null && aux2.getName().equals(aux.getBrotherNameNode())
                             && (aux.getModificationType() == ParameterDiff.Modification.NEW)){
                            //si es del mismo tipo y new lo agrego a la lista de hermanos
                            it.next();
                            childParameters.add(aux);
                        }else{
                            break loop;
                        }
                    }
                    parameterDiff.setModifiedAtributesList(childParameters);

                    if (parameterDiff.getBrotherNameNode() == null) {
                    	diffCodes.add(XSLTemplateFunction.getAddFirstParameterTemplate(prefix, operation.getName(), childParameters));
                    } else {
                    	parameterdiffs = matchedAttributes.get(parameterDiff.getBrotherNameNode());
        				if (parameterdiffs == null){
        					parameterdiffs = new LinkedList<ParameterDiff>();
        					parameterdiffs.add(parameterDiff);
        					matchedAttributes.put(parameterDiff.getBrotherNameNode(), parameterdiffs);
        					diffCodes.add(XSLTemplateFunction.getAddParameterTemplate(prefix, operation.getName(), parameterDiff.getBrotherNameNode(), childParameters));
        				}else{
        					if (parameterdiffs.size() == 1 && 
        						(parameterdiffs.get(0).getModificationType() == ParameterDiff.Modification.RENAME || parameterdiffs.get(0).getModificationType() == ParameterDiff.Modification.DELETE)){
        						parameterdiffs.add(parameterDiff);
        						matchedAttributes.put(parameterDiff.getBrotherNameNode(), parameterdiffs);
        						String sameMatched = parameterdiffs.get(0).getName();
        						if (parameterdiffs.get(0).getModificationType() == ParameterDiff.Modification.RENAME){
        							String newName = parameterdiffs.get(0).getNewName();
        							String renameAndAdd = XSLTemplateFunction.getRenameAndAddSameMatchedParameterTemplate(prefix, operation.getName(),sameMatched, childParameters, newName);
        							replaceParameterDiffCode(prefix, operation.getName(),parameterDiff.getBrotherNameNode(),diffCodes,renameAndAdd);
        						}else{
        							//ParameterDiff.Modification.DELETE
        							String deleteAndAdd = XSLTemplateFunction.getDeleteAndAddSameMatchedParameterTemplate(prefix, operation.getName(),sameMatched,childParameters);
        							replaceParameterDiffCode(prefix, operation.getName(),parameterDiff.getBrotherNameNode(),diffCodes,deleteAndAdd);
        						}
        					}else{
        						logger.log(Level.SEVERE, "malformed parameter diffs in operation {0}", operation.getName());
        					}
        				} 
                    }
                break;

                case RENAME:
                	parameterdiffs = matchedAttributes.get(parameterDiff.getName());
    				if (parameterdiffs == null){
    					parameterdiffs = new LinkedList<ParameterDiff>();
    					parameterdiffs.add(parameterDiff);
    					matchedAttributes.put(parameterDiff.getName(), parameterdiffs);
    					diffCodes.add(XSLTemplateFunction.getRenameParameterTemplate(prefix, operation.getName(),parameterDiff.getName(),parameterDiff.getNewName()));
    				}else{
    					if (parameterdiffs.size() == 1 && parameterdiffs.get(0).getModificationType() == ParameterDiff.Modification.NEW){
    						parameterdiffs.add(parameterDiff);
    						matchedAttributes.put(parameterDiff.getName(), parameterdiffs);						
    						String sameMatched = parameterDiff.getName();
    						String newName = parameterDiff.getNewName();
    						String renameAndAdd = XSLTemplateFunction.getRenameAndAddSameMatchedParameterTemplate(prefix, operation.getName(),sameMatched, parameterdiffs.get(0).getModifiedAtributesList(), newName);
							replaceParameterDiffCode(prefix, operation.getName(),parameterDiff.getName(),diffCodes,renameAndAdd);
    						
    					}else{
    						logger.log(Level.SEVERE, "malformed parameter diffs in operation {0}", operation.getName());
    					}
    				}
                break;

                case DELETE:
                	parameterdiffs = matchedAttributes.get(parameterDiff.getName());
    				if (parameterdiffs == null){
    					parameterdiffs = new LinkedList<ParameterDiff>();
    					parameterdiffs.add(parameterDiff);
    					matchedAttributes.put(parameterDiff.getName(), parameterdiffs);
    					diffCodes.add(XSLTemplateFunction.getDeleteParameterTemplate(prefix, operation.getName(),parameterDiff.getName()));
    				}else{
    					if (parameterdiffs.size() == 1 && parameterdiffs.get(0).getModificationType() == ParameterDiff.Modification.NEW){
    						parameterdiffs.add(parameterDiff);
    						matchedAttributes.put(parameterDiff.getName(), parameterdiffs);
    						String sameMatched = parameterDiff.getName();
    						String deleteAndAdd = XSLTemplateFunction.getDeleteAndAddSameMatchedParameterTemplate(prefix, operation.getName(),sameMatched, parameterdiffs.get(0).getModifiedAtributesList());
							replaceParameterDiffCode(prefix, operation.getName(), parameterDiff.getName(),diffCodes,deleteAndAdd);	
    					}else{
    						logger.log(Level.SEVERE, "malformed parameter diffs in operation {0}", operation.getName());
    					}
    				}              	
                break;
               
            }
        }
        for (String diffCode : diffCodes){
        	fileContent.append(diffCode);
        }
    }
    
    private void replaceParameterDiffCode(String prefix, String functionName, String matchedAttibute,List<String> diffCodes, String newDiffCode){
    	for(int i = 0 ; i< diffCodes.size(); i++){
    		String match = "match=\""+ prefix +":"+ functionName + "/" + matchedAttibute +"\"";
    		String diffCode =  diffCodes.get(i);
    		if (diffCode.contains(match)){
    			diffCodes.set(i,newDiffCode);
    			break;
    		}
    	}
    }
  
}
